package com.leo.train.business.service.Impl;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.fastjson.JSON;
import com.leo.train.business.dto.ConfirmOrderSaveDto;
import com.leo.train.business.dto.ConfirmOrderTicketDto;
import com.leo.train.business.entity.ConfirmOrder;
import com.leo.train.business.mapper.ConfirmOrderMapper;
import com.leo.train.business.service.BeforeConfirmOrderService;
import com.leo.train.business.service.ConfirmOrderService;
import com.leo.train.business.service.SkTokenService;
import com.leo.train.common.context.LoginMemberContext;
import com.leo.train.common.enums.ConfirmOrderStatusEnum;
import com.leo.train.common.exception.BusinessException;
import com.leo.train.common.exception.BusinessExceptionEnum;
import com.leo.train.common.util.SnowUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;

@Service
@Slf4j
public class BeforeConfirmOrderServiceImpl implements BeforeConfirmOrderService {
    @Autowired
    private SkTokenService skTokenService;
    @Autowired
    private ConfirmOrderService confirmOrderService;
    @Autowired
    private ConfirmOrderMapper confirmOrderMapper;
    /**
     * 预处理订单，返回订单id
     * 购票前：令牌校验，生成订单
     * @param confirmOrderSaveDto
     * @return
     */
    @Override
    @SentinelResource(value = "beforeDoConfirm", blockHandler = "beforeDoConfirmBlock")
    public Long beforeDoConfirm(ConfirmOrderSaveDto confirmOrderSaveDto) {
        Long id = null; // 订单id
        // 根据前端传值，加入排队人数，+1是因为最后这个人是自己，前边的都是测试的排队人数
        for (int i = 0; i < confirmOrderSaveDto.getLineNumber() + 1; i++) {
            // 1. 校验令牌余量
            boolean validSkToken = skTokenService.validSkToken(confirmOrderSaveDto.getDate(), confirmOrderSaveDto.getTrainCode(), LoginMemberContext.getId());
            if (!validSkToken) {
                log.info("令牌校验失败");
                throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_SK_TOKEN_FAIL);
            } else {
                log.info("令牌校验通过");
            }
            // 2. 生成订单,并保存到数据库
            Date now = new Date();
            List<ConfirmOrderTicketDto> tickets = confirmOrderSaveDto.getTickets(); // 获取车票信息
            // 取出4个唯一值
            String trainCode = confirmOrderSaveDto.getTrainCode();
            String start = confirmOrderSaveDto.getStart();
            String end = confirmOrderSaveDto.getEnd();
            Date date = confirmOrderSaveDto.getDate();
            ConfirmOrder confirmOrder = new ConfirmOrder();
            confirmOrder.setId(SnowUtil.getSnowflakeNextId());
            confirmOrder.setMemberId(LoginMemberContext.getId()); // 获取当前登录用户ID（从线程中取）
            confirmOrder.setDate(date);
            confirmOrder.setTrainCode(trainCode);
            confirmOrder.setStart(start);
            confirmOrder.setEnd(end);
            confirmOrder.setDailyTrainTicketId(confirmOrderSaveDto.getDailyTrainTicketId());
            confirmOrder.setTickets(JSON.toJSONString(tickets)); // 需要将集合转为JSON字符串
            confirmOrder.setStatus(ConfirmOrderStatusEnum.INIT.getCode()); // 初始状态
            confirmOrder.setCreateTime(now);
            confirmOrder.setUpdateTime(now);
            confirmOrder.setLogId(confirmOrderSaveDto.getLogId());
            confirmOrderMapper.insert(confirmOrder);

            // 3.最后：异步调用购票的逻辑
            confirmOrderService.doConfirm(confirmOrder);

            // 4. 返回订单id,供前端采用轮询查询订单状态
            id = confirmOrder.getId();
        }
        return id;
    }

    /**
     * 降级方法，需包含限流方法的所有参数和BlockException参数
     * @param confirmOrderSaveDto
     * @param blockException
     */
    public void beforeDoConfirmBlock(ConfirmOrderSaveDto confirmOrderSaveDto, BlockException blockException) {
        log.info("购票请求被限流，confirmOrderSaveDto={}", JSON.toJSONString(confirmOrderSaveDto));
        throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_FLOW_EXCEPTION);
    }
}
